﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using UTable.Core.Configuration;
using UTable.Core.Input;
using UTable.Core.Objects;
using UTable.Core.Objects.Runtime;
using UTable.Core.Objects.Controls.Tabletop;
using Microsoft.Multipoint.Sdk;

namespace UTable.Core.Windows
{
    /// <summary>
    /// Define an application based on UTableSDK
    /// </summary>
    public class UApplication : Application
    {
        #region Private Members

        /// <summary>
        /// System MainWindow
        /// </summary>
        private Window window;

        /// <summary>
        /// Container of MainWindow
        /// </summary>
        private Canvas host;

        /// <summary>
        /// The root of ObjectTree
        /// </summary>
        private UTabletop root;

        #endregion

        #region Properties

        /// <summary>
        /// Get the <see cref="System.Windows.Window"/> for displaying the tabletop environment and applications on it.
        /// </summary>
        public new Window MainWindow
        {
            get { return window; }
        }

        /// <summary>
        /// Container for the MainWindow
        /// </summary>
        public Canvas Host
        {
            get { return host; }
        }

        /// <summary>
        /// The tabletop of the UApplication
        /// </summary>
        public UTabletop Root
        {
            get { return root; }
        }

        #endregion

        #region Overrides

        /// <summary>
        /// Initialize UApplication when StartUp
        /// </summary>
        /// <param name="e"></param>
        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);

            UTableHelper.Configuration = GetConfiguration();

            // Build ObjectTree, actually created inside
            UObjectTree uObjectTree = UObjectTree.Instance;
            UTableHelper.RegisterObjectTree(uObjectTree);

            // Show ObjectTree
            InitializeMainWindow();
            host = new Canvas();
            MainWindow.Content = Host;
            MainWindow.Show();
            
            // Create Input Manager
            InputManager inputManager = new InputManager();
            inputManager.ObjectTree = uObjectTree;

            // Create Object Manager
            ObjectManager objectManager = new ObjectManager();
            objectManager.InputManager = inputManager;
            objectManager.ObjectTree = uObjectTree;
            UTableHelper.RegisterObjectManager(objectManager);

            //TODO Create First Object, i.e. Tabletop
            root = new UTabletop(UTableHelper.Configuration.Tabletop.Class.Type);
            root.Width = System.Windows.SystemParameters.PrimaryScreenWidth;
            root.Height = System.Windows.SystemParameters.PrimaryScreenHeight;
            root.Position = new Point(0, 0);
            root.Loaded += RootLoaded;
            uObjectTree.Root = root;
            Host.Children.Insert(0, root);

            // Create Input Providers
            IInputProvider inputProvider = GetMultiTouchProvider();
            //TODO UTableHelper.RegisterInputProvider()
            //TODO UTableHelper.RegisterInputMapper()

            ConfigurationXMLSerializer.Save(UTableHelper.Configuration, "UTable.config");
        }

        protected override void OnExit(ExitEventArgs e)
        {
            base.OnExit(e);
            MultipointSdk.Instance.Dispose();
            OnTabletopExit(e);
        }

        #endregion

        #region Private Methods

        #region Initialization Methods

        /// <summary>
        /// Set size of window according to Configuration
        /// </summary>
        private void InitializeMainWindow()
        {
            window = new Window();
            window.WindowStyle = WindowStyle.None;
            if (UTableHelper.Configuration.Window.Maximized)
                window.WindowState = WindowState.Maximized;
            else
            {
                window.Width = UTableHelper.Configuration.Window.Width;
                window.Height = UTableHelper.Configuration.Window.Height;
                window.Left = UTableHelper.Configuration.Window.Left;
                window.Top = UTableHelper.Configuration.Window.Top;
                window.Topmost = true;
            }
            
            window.AllowsTransparency = true;
            window.Background = new SolidColorBrush(Colors.Transparent);
        }

        #endregion

        #endregion

        #region Virtual Methods

        /// <summary>
        /// Return Configuration for UTable. Can be override.
        /// </summary>
        /// <returns></returns>
        protected virtual Configuration.Configuration GetConfiguration()
        {
            //return Configuration.Configuration.GetConfigurationFromXml("UTable.config");
            return Configuration.Configuration.GetDefaultConfiguration();
        }

        protected virtual IInputProvider GetMultiTouchProvider()
        {
            CompositeInputProvider providers = new CompositeInputProvider();
            foreach (ProviderConfiguration provider in UTableHelper.Configuration.Input.Providers)
            {
                IInputProvider input = LoadInputProvider(provider);
                if (input != null)
                    providers.Providers.Add(input);
            }
            return providers;
        }

        /// <summary>
        /// Leave to developers to override it, called in OnExit
        /// </summary>
        /// <param name="e"></param>
        protected virtual void OnTabletopExit(ExitEventArgs e) { }

        /// <summary>
        /// Leave to developers to override it, called after Tabletop finishes loading
        /// </summary>
        /// <param name="e"></param>
        protected virtual void OnTabletopLoaded(RoutedEventArgs e) { }

        #endregion

        #region Private Methods

        private IInputProvider LoadInputProvider(ProviderConfiguration providerConfiguration)
        {
            IInputProvider provider = providerConfiguration.Instance as IInputProvider;
            if (provider != null)
                provider.Initialize(this);
            return provider;
        }

        private void RootLoaded(object sender, RoutedEventArgs e)
        {
            OnTabletopLoaded(e);
        }

        #endregion
    }
}
